﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using MOJsdk;
using System.Threading;

namespace AndroidRemote
{
    /// <summary>
    /// Manage a communication using sockets, message style [A][B][C]
    /// A-Action (6bytes)
    /// B-Arg Size (6bytes)
    /// c-Argument (B size)
    /// </summary>
    public class AsyncSocketServer
    {
        public static ManualResetEvent allDone = new ManualResetEvent(false);

        public static void StartListening()
        {
            NoArgDelegate th = new NoArgDelegate(AsyncStartListening);
            th.BeginInvoke(null, null);
        }
        public static void AsyncStartListening()
        {
            int port = Settings.ServerPort.Value();

            IPEndPoint _endPoint = new IPEndPoint(TOOLS.GetLocalIP(), port);
            Settings.ServerAdress.Set(_endPoint.Address.ToString());

            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                listener.Bind(_endPoint);
                listener.Listen(5000);

                while (true)
                {
                    allDone.Reset();

                    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

                    allDone.WaitOne();
                }
            }
            catch (Exception ex)
            {
                MojHost.GetHost.ReportError(new ErrorReporter()
                {
                    ErrorCode = "AsyncSocketServer.StartListening",
                    ErrorDetails = ex.ToString(),
                    ErrorMessage = "Server Error",
                });

                StartListening();
            }
            finally
            {
                try { listener.Close(); }
                catch { }
            }
        }

        public static void AcceptCallback(IAsyncResult ar)
        {
            allDone.Set();

            Socket listener = (Socket)ar.AsyncState;
            Socket handler = listener.EndAccept(ar);

            SocketRequest Request = GetRequestData(handler);

            byte[] response = ServerActions.ExecuteAction(Request.Action, Request.Argument);
            handler.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), handler);
        }

        public static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                handler.EndSend(ar);

                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
            }
            catch(Exception ex)
            {
                MojHost.GetHost.ReportError(new ErrorReporter()
                {
                    ErrorCode = "AsyncSocketServer.SendCallback",
                    ErrorDetails = ex.ToString(),
                    ErrorMessage = "Error sending response",
                });
            }
        }

        public static SocketRequest GetRequestData(Socket Handler)
        {
            SocketRequest request = new SocketRequest();

            byte[] action = new byte[6];
            byte[] argsize = new byte[6];
            byte[] arg = null;

            Handler.Receive(action);
            Handler.Receive(argsize);

            int size = Encoding.ASCII.GetString(argsize).ToInt();
            if (size > 0)
            {
                arg = new byte[size];
                Handler.Receive(arg);
            }

            request.Action = action;
            request.Argument = arg;

            return request;
        }
    }

    public class SocketRequest
    {
        public byte[] Action { get; set; }
        public byte[] Argument { get; set; }
    }
}
